#pragma once
#include <future>
#include <mutex>
#include <vector>
#include <memory>
#include <map>
#include "threadpool.hpp"

namespace hhfox {

	typedef std::string RequestID;

	template<typename Ret>
	class Request;

	template<typename T>
	class RequestCenter :public std::enable_shared_from_this<RequestCenter<T>> {
		friend Request<T>;

	public:
		RequestCenter(size_t threadNum = std::thread::hardware_concurrency()):pool_(threadNum) {}

		template<typename Fn, typename ...Args>
		std::shared_ptr<Request<T>> createRequest(const RequestID& reqid,Fn&& f, Args&& ...args) {
			auto request = std::make_shared<Request<T>>(pool_,*this, reqid);
			request->run(std::forward<Fn>(f), std::forward<Args>(args)...);
			return  request;
		}

		void emitResult(const RequestID& reqid, const T& data) {
			std::unique_lock<std::mutex> lock(center_mtx_);
			for (auto& it : center_) {
				if (it.second.lock()->getReqId() == reqid) {
					it.second.lock()->onResult(data);
				}
			}
		}

		~RequestCenter() {
			std::unique_lock<std::mutex> lock(center_mtx_);
			center_.clear();
		}
	private:
		void add_watcher(const RequestID& id,std::shared_ptr<Request<T>> ptr) {
			std::unique_lock<std::mutex> lock(center_mtx_);
			center_.insert({ id,ptr });
		}

		void remove_watcher(const RequestID& id) {
			std::unique_lock<std::mutex> lock(center_mtx_);
			center_.erase(id);
		}
	private:
		ThreadPool pool_;
		std::mutex center_mtx_;
		std::map<RequestID, std::weak_ptr<Request<T>>> center_;
	};

	template<typename Ret>
	class Request:public std::enable_shared_from_this<Request<Ret>>{
		friend RequestCenter<Ret>;

	public:
		Request(ThreadPool& pool, RequestCenter<Ret>& center, const RequestID& reqid)
			:pool_(pool), center_(center),reqid_(reqid){

		}

		RequestID getReqId() const {
			return reqid_;
		}

		auto getResult() {
			return result_.get_future();
		}

		auto getShareResult() {
			return result_.get_future().share();
		}

		~Request() {
			center_.remove_watcher(reqid_);
		}

	private:
		template<typename Fn,typename ...Args>
		auto run(Fn&& f,Args&& ...args) {
			center_.add_watcher(reqid_,std::enable_shared_from_this<Request<Ret>>::shared_from_this());
			return pool_.enqueue(std::forward<Fn>(f), std::forward<Args>(args)...);
		}
	
		void onResult(const Ret& data) {
			result_.set_value(data);
		}

	private:
		ThreadPool& pool_;
		RequestCenter<Ret>& center_;
		RequestID reqid_;
		std::promise<Ret> result_;
	};

}
